// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Xml;

namespace SoapTrace.Core.Formatters
{
	/// <summary>
	/// Represents a wsdl file.
	/// </summary>
	public class WebServicesDescriptionLanguageFile
	{
		/// <summary>
		/// The web service in the wsdl file.
		/// </summary>
		private WebService m_WebService = new WebService( );

		/// <summary>
		/// The namespace manager for the loaded wsdl file.
		/// </summary>
		private XmlNamespaceManager m_NamespaceManager;

		/// <summary>
		/// Wsdl namespace prefix to use.
		/// </summary>
		private string m_WsdlNamespacePrefix = "wsdl";

		/// <summary>
		/// Soap namespace prefix to use.
		/// </summary>
		private string m_SoapNamespacePrefix = "soap";

		/// <summary>
		/// The Xml schema namespace prefix to use.
		/// </summary>
		private string m_SchemaNamespacePrefix = "s";
		
		/// <summary>
		/// String in xml schema that represents an array.
		/// </summary>
		private string m_ArrayOfType = "ArrayOf";

		/// <summary>
		/// Creates a new instance of the 
		/// <see cref="WebServicesDescriptionLanguageFile"/> class.
		/// </summary>
		public WebServicesDescriptionLanguageFile( )
		{
		}

		/// <summary>
		/// Loads the wsdl file.
		/// </summary>
		/// <param name="filename">The filename of the wsdl file.</param>
		public void Load( string filename )
		{
			XmlDocument Document = new XmlDocument( );
			Document.Load( filename );

			// Set up namespaces.

			SetUpNamespaces( Document.NameTable );

			ParseWsdl( Document.DocumentElement );
		}
		
		/// <summary>
		/// Loads a wsdl file from a <see cref="System.IO.TextReader"/> object.
		/// </summary>
		/// <param name="reader">The <see cref="System.IO.TextReader"/>
		/// object to read the wsdl string from.</param>
		public void Load( TextReader reader )
		{
			XmlDocument Document = new XmlDocument( );
			Document.Load( reader );
			
			// Set up namespaces.

			SetUpNamespaces( Document.NameTable );

			ParseWsdl( Document.DocumentElement );			
		}
		
		/// <summary>
		/// Loads wsdl from a url.
		/// </summary>
		/// <param name="url">The url to load the wsdl from.</param>
		public void LoadFromUrl( string url )
		{
			// Request the wsdl information.
			
			HttpWebRequest Request = ( HttpWebRequest )WebRequest.Create( url );
			Request.Method = "GET";

			// Read the response.
			
			HttpWebResponse Response = ( HttpWebResponse )Request.GetResponse( );
			
			try
			{

				Stream ResponseStream = Response.GetResponseStream( );

				StreamReader Reader = new StreamReader( ResponseStream,
					System.Text.Encoding.ASCII );

				Load( ( TextReader )Reader );
			}
			finally
			{
				Response.Close( );
			}
		}

		/// <summary>
		/// The web service specified in the Wsdl file.
		/// </summary>
		public WebService WebService
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_WebService;
			}
		}

		/// <summary>
		/// Parses the wsdl file.
		/// </summary>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		private void ParseWsdl( XmlElement element )
		{
			// Get the web service properties.

			m_WebService.Name = GetWebServiceName( element );
			m_WebService.Uri = GetWebServiceUri( element );

			// Get the web service operations.

			string[ ] Operations = GetWebServiceOperations( element );

			// Get each operation's parameters and return values.

			foreach( string OperationName in Operations )
			{
				WebServiceOperation Operation = new WebServiceOperation( );
				Operation.Name = OperationName;

				// Get the parameters.

				WebServiceParameter[ ] Parameters = null;
				
				bool IsDocumentLiteral = IsDocumentLiteralOperation( OperationName, element );

				if( IsDocumentLiteral )
				{
					Parameters = GetDocumentLiteralWebServiceParameters( OperationName, 
						element );
				}
				else
				{
					Parameters = GetRpcWebServiceParameters( OperationName, element );
				}
				
				Operation.Parameters.Add( Parameters );

				// Get the return value.
								
				if( IsDocumentLiteral )
				{
					Operation.ReturnValue = 
						GetDocumentLiteralWebServiceReturnValue( OperationName, element );
				}
				else
				{
					Operation.ReturnValue = GetRpcWebServiceReturnValue( OperationName, element );
				}
				
				m_WebService.Operations.Add( Operation );
			}
		}

		/// <summary>
		/// Gets the web service name from the loaded Wsdl.
		/// </summary>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <remarks>
		///		<para>The web service name exists in the wsdl file in the
		///	service element.</para>
		///		<code>
		///		<definitions>
		///			<service name="MathsService">
		///				...
		///			</service>
		///		</definitions>
		///		</code>
		/// </remarks>
		/// <returns>The web service name.</returns>
		private string GetWebServiceName( XmlElement element )
		{
			string Xpath = String.Concat( m_WsdlNamespacePrefix, ":service/@name" );
			XmlAttribute WebServiceNameAttribute = 
				( XmlAttribute )element.SelectSingleNode( Xpath, m_NamespaceManager );

			return WebServiceNameAttribute.Value;
		}

		/// <summary>
		/// Gets the web service Uri from the loaded Wsdl.
		/// </summary>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>The web service uri.</returns>
		/// <remarks>
		///		<para>The web service uri exists in the wsdl file in the location 
		///		attribute of the soap:address element.</para>
		///		<code>
		///		<definitions>
		///			<service name="MathsService">
		///				<port name="MathsServiceSoap" binding="s0:MathsServiceSoap">
		///					<address location="http://myserver/MathWebService/Maths.asmx" />
		///				</port>
		///			</service>
		///		</definitions>
		///		</code>
		/// </remarks>
		private string GetWebServiceUri( XmlElement element )
		{
			string Xpath = String.Concat( m_WsdlNamespacePrefix, ":service/",
				m_WsdlNamespacePrefix, ":port/",
				m_SoapNamespacePrefix, ":address/@location" );

			XmlAttribute WebServiceUriAttribute = 
				( XmlAttribute )element.SelectSingleNode( Xpath, m_NamespaceManager );

			return ExtractUri( WebServiceUriAttribute.Value );
		}

		/// <summary>
		/// Removes the http://servername:port from the uri string.
		/// </summary>
		/// <param name="fullUri">A uri string of the form http://servername:port/test/ </param>
		/// <returns>The uri part without any server information.</returns>
		private static string ExtractUri( string fullUri )
		{
			string Uri = Regex.Replace( fullUri, @"^http://.*?/", "/" );
			return Uri;
		}

		/// <summary>
		/// Gets the web service operations.
		/// </summary>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>All the operations for the web service.</returns>
		/// <remarks>
		///		<para>The web service operations exist as multiple 
		///		portType/operation elements in the wsdl file</para>
		///		<code>
		///		<definitions>
		///			<portType name="TestEJBPort">
		///				<operation name="method1">
		///					...
		///				</operation>
		///			</portType>
		///		</definitions>
		///		</code>
		/// </remarks>
		private string[ ] GetWebServiceOperations( XmlElement element )
		{
			// Get all the operation elements.

			string Xpath = String.Concat( m_WsdlNamespacePrefix, ":portType/", 
				m_WsdlNamespacePrefix, ":operation/@name" );
			
			XmlNodeList OperationNodes = 
				( XmlNodeList )element.SelectNodes( Xpath, m_NamespaceManager );

			// Get the operation names.

			string[ ] Operations = new string[ OperationNodes.Count ];

			for( int i = 0; i < Operations.Length; ++i )
			{
				XmlAttribute NameAttribute = ( XmlAttribute )OperationNodes[ i ];
				Operations[ i ] = NameAttribute.Value;
			}

			return Operations;
		}

		/// <summary>
		/// Gets the RPC web service parameters based on the operation name.
		/// </summary>
		/// <param name="operationName">The web service operation name.</param>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>An array of web service parameters for the specifed
		/// web service operation.</returns>
		/// <remarks>
		///		<para>The web service parameters exist as multiple 
		///		message/part elements in the wsdl file.</para>
		///		<code>
		///		<definitions>
		///			<message name="method1">
		///				<part type="partns:string" name="string"/>
		///				<part type="partns:string" name="string0"/>
		///			</message>
		///		</definitions>
		///		</code>
		///		<para>The web service parameters are linked to
		///		the web service operation via the input and output elements
		///		that are children of the operation element.</para>
		///		<code>
		///		<definitions>
		///			<portType name="TestEJBPort">
		///				<operation name="method1">
		///					<input message="tns:method1"/>
		///					<output message="tns:method1Response"/>
		///				</operation>
		///			</portType>
		///		</definitions>
		///		</code>
		/// </remarks>		
		private WebServiceParameter[ ] GetRpcWebServiceParameters( string operationName, 
			XmlElement element )
		{
			// Get the name of the message element for this operation's
			// parameters.
			string MessageElementName = 
				GetParameterMessageElementName( operationName,
				element );

			// Get the message element matching the operation name.
			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":message[@name='", MessageElementName, "']/",
				m_WsdlNamespacePrefix, ":part" );

			XmlNodeList MessageParts = element.SelectNodes( Xpath, m_NamespaceManager );

			// Extract the web service parameter info.

			WebServiceParameter[ ] Parameters = 
				new WebServiceParameter[ MessageParts.Count ];

			for( int i = 0; i < MessageParts.Count; ++i )
			{
				XmlElement MessagePartElement = ( XmlElement )MessageParts[ i ];

				WebServiceParameter Parameter = 
					 GetParameterFromMessagePart( MessagePartElement );

				Parameters[ i ] = Parameter;
			}

			return Parameters;
		}

		/// <summary>
		/// Gets the message element name holding the parameter information
		/// for the specified web service operation.
		/// </summary>
		/// <param name="operationName">The name of the web service operation.</param>
		/// <param name="element">The root element of the loaded wsdl file.</param>
		/// <returns>The name of the message element holding the
		/// parameter information for this web service operation.</returns>
		private string GetParameterMessageElementName( string operationName,
			XmlElement element )
		{
			string MessageElementName = String.Empty;

			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":portType/",
				m_WsdlNamespacePrefix, 
				":operation[@name='", operationName, "']/",
				m_WsdlNamespacePrefix,
				":input/@message" );


			XmlAttribute MessageAttribute = ( XmlAttribute )element.SelectSingleNode( Xpath, 
				m_NamespaceManager );

			if( MessageAttribute != null )
			{
				// Remove namespace prefix from name.

				MessageElementName = RemoveNamespacePrefix( MessageAttribute.Value );
			}

			return MessageElementName;
		}

		/// <summary>
		/// Gets the parameter information from the message part element.
		/// </summary>
		/// <param name="messagePartElement">A message part element.</param>
		/// <returns>A web service parameter.</returns>
		private WebServiceParameter GetParameterFromMessagePart( XmlElement messagePartElement )
		{
			XmlAttribute ParameterTypeAttribute = messagePartElement.Attributes[ "type" ];
			XmlAttribute ParameterNameAttribute = messagePartElement.Attributes[ "name" ];

			WebServiceParameter Parameter = new WebServiceParameter( );
			Parameter.Name = ParameterNameAttribute.Value;

			if( ParameterTypeAttribute != null )
			{
				string ParameterType = RemoveNamespacePrefix( ParameterTypeAttribute.Value ); 
				
				if( ParameterType.StartsWith( m_ArrayOfType ) )
				{
					ParameterType = ParameterType.Remove( 0, m_ArrayOfType.Length );
					Parameter.ParameterType = 
						String.Concat( ParameterType.ToLower( CultureInfo.InvariantCulture ), ".array" );
				}
				else
				{
					Parameter.ParameterType = ParameterType;
				}
			}

			return Parameter;
		}

		/// <summary>
		/// Gets the document literal web service parameters based on the operation name.
		/// </summary>
		/// <param name="operationName">The web service operation name.</param>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>An array of web service parameters for the specifed
		/// web service operation.</returns>
		/// <remarks>
		///		<para>The web service parameters exist a single 
		///		message/part element in the wsdl file which points to
		///		a complex type.</para>
		///		<code>
		///		<definitions>
		///			<message name="AddSoapIn">
		///				<part name="parameters" element="s0:AddResponse" />
		///			</message>
		///		</definitions>
		///		</code>
		///		<para>The web service parameters are linked to
		///		the web service operation via the input and output elements
		///		that are children of the operation element.</para>
		///		<code>
		///		<definitions>
		///			<portType name="MathsServiceSoap">
		///				<operation name="Add">
		///					<input message="s0:AddSoapIn" />
		///					<output message="s0:AddSoapOut" />
		///				</operation>
		///			</portType>
		///		</definitions>
		///		</code>
		/// </remarks>		
		private WebServiceParameter[ ] GetDocumentLiteralWebServiceParameters( string operationName, 
			XmlElement element )
		{
			// Get the name of the message element for this operation's
			// parameters.
			string MessageElementName = 
				GetParameterMessageElementName( operationName,
				element );

			// Get the message element matching the operation name.
			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":message[@name='", MessageElementName, "']/",
				m_WsdlNamespacePrefix, ":part" );

			XmlElement MessagePart = 
				( XmlElement )element.SelectSingleNode( Xpath, m_NamespaceManager );

			string TypeName = GetTypeFromMessagePart( MessagePart );

			// Extract the web service parameter info.

			WebServiceParameter[ ] Parameters = 
				GetParametersFromSchemaElement( TypeName, element );

			return Parameters;
		}

		/// <summary>
		/// Determines whether the specified operation is document literal
		/// or an RPC operation.
		/// </summary>
		/// <param name="name">The name of the web service operation.</param>
		/// <param name="element">The root element of the loaded wsdl.</param>
		/// <returns>A flag indicating whether the operation is 
		/// document literal.</returns>
		/// <remarks>
		/// <para>The operation type is to be found in the style attribute:</para>
		/// <para>
		/// <code>
		///		<binding name="MathsServiceSoap" type="s0:MathsServiceSoap">
		///			<operation name="Add">
		///				<operation soapAction="http://myuri.org/Add" style="document" />
		///			</operation>
		///		</binding>	
		///	</code>
		/// </para></remarks>
		private bool IsDocumentLiteralOperation( string name, XmlElement element )
		{
			bool IsDocumentLiteral = false;

			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":binding/",
				m_WsdlNamespacePrefix,
				":operation[@name='", name, "']/",
				m_SoapNamespacePrefix, 
				":operation/@style" );

			XmlAttribute StyleAttribute = 
				( XmlAttribute )element.SelectSingleNode( Xpath, m_NamespaceManager );

			if( StyleAttribute != null )
			{
				if( String.Compare( StyleAttribute.Value, "document", true, CultureInfo.InvariantCulture ) == 0 )
				{
					IsDocumentLiteral = true;
				}
			}

			return IsDocumentLiteral;
		}

		/// <summary>
		/// Gets the name of the type that the message part element
		/// points to via its element attribute.
		/// </summary>
		/// <param name="messagePartElement"></param>
		/// <returns>The name of the type that the message element
		/// points to.</returns>
		private static string GetTypeFromMessagePart( XmlElement messagePartElement )
		{
			XmlAttribute ParameterElementAttribute = 
				messagePartElement.Attributes[ "element" ];

			string TypeName = String.Empty;

			if( ParameterElementAttribute != null )
			{
				TypeName = RemoveNamespacePrefix( ParameterElementAttribute.Value );
			}

			return TypeName;
		}

		/// <summary>
		/// Removes any namespace prefix from the string.
		/// </summary>
		/// <param name="prefixedString">The prefixed string.</param>
		/// <returns>A string with the xml namespace removed.</returns>
		private static string RemoveNamespacePrefix( string prefixedString )
		{
			return Regex.Replace( prefixedString, @"^.*?:", "" ); ;
		}

		/// <summary>
		/// Gets the web service parameters from the schema element.
		/// </summary>
		/// <param name="schemaType">The name of the schema element to get
		/// the parameters from.</param>
		/// <param name="element">The root element of the wsdl document.</param>
		/// <returns>An array of web service parameters.</returns>
		/// <remarks><para>The parameter information is held
		/// in one of the types defined in the definitions/types elements.</para>
		/// <para>
		/// <code>
		/// <definitions>
		///		<types>
		///			<schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
		///				<element name="Add">
		///					<complexType>
		///						<sequence>
		///							<element minOccurs="1" maxOccurs="1" name="value1" type="s:int" />
		///							<element minOccurs="1" maxOccurs="1" name="value2" type="s:int" />
		///						</sequence>
		///					</complexType>
		///				</element>
		///			</schema>
		///		</types>
		///	</definitions>
		///	</code>
		///	</para></remarks>
		private WebServiceParameter[ ] GetParametersFromSchemaElement( string schemaType, 
			XmlElement element )
		{

			string Xpath = String.Concat( m_WsdlNamespacePrefix,
				":types/",
				m_SchemaNamespacePrefix,
				":schema/",
				m_SchemaNamespacePrefix,
				":element[ @name='", schemaType, "' ]//",
				m_SchemaNamespacePrefix,
				":element" );

			XmlNodeList ParameterNodes = element.SelectNodes( Xpath, m_NamespaceManager );

			WebServiceParameter[ ] Parameters = new WebServiceParameter[ ParameterNodes.Count ];
			
			// Get parameter name and type.

			for( int i = 0; i < ParameterNodes.Count; ++i )
			{
				XmlElement ParameterElement = ( XmlElement )ParameterNodes[ i ];

				XmlAttribute ParameterTypeAttribute = ParameterElement.Attributes[ "type" ];
				XmlAttribute ParameterNameAttribute = ParameterElement.Attributes[ "name" ];

				WebServiceParameter Parameter = new WebServiceParameter( );

				Parameter.Name = ParameterNameAttribute.Value;

				string ParameterType = RemoveNamespacePrefix( ParameterTypeAttribute.Value );
	
				if( ParameterType.StartsWith( m_ArrayOfType ) )
				{
					ParameterType = ParameterType.Remove( 0, m_ArrayOfType.Length );
					Parameter.ParameterType = 
						String.Concat( ParameterType.ToLower( CultureInfo.InvariantCulture ), ".array" );
				}
				else
				{
					Parameter.ParameterType = ParameterType;
				}

				Parameters[ i ] = Parameter;
			}

			return Parameters;
		}
		
		/// <summary>
		/// Sets up the namespace manager using the XmlNameTable.
		/// </summary>
		/// <param name="nameTable">The XmlNameTable to use.</param>
		private void SetUpNamespaces( XmlNameTable nameTable )
		{
			m_NamespaceManager = new XmlNamespaceManager( nameTable );

			m_NamespaceManager.AddNamespace( m_WsdlNamespacePrefix,  
				"http://schemas.xmlsoap.org/wsdl/" );

			m_NamespaceManager.AddNamespace( m_SoapNamespacePrefix,
				"http://schemas.xmlsoap.org/wsdl/soap/" );

			m_NamespaceManager.AddNamespace( m_SchemaNamespacePrefix,
				"http://www.w3.org/2001/XMLSchema" );			
		}
		
		/// <summary>
		/// Gets the document literal web service return value based on 
		/// the operation name.
		/// </summary>
		/// <param name="operationName">The web service operation name.</param>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>A web service return value for the specifed
		/// web service operation.</returns>
		private WebServiceReturnValue GetDocumentLiteralWebServiceReturnValue( string operationName, 
			XmlElement element )
		{
			// Get the name of the message element for this operation's
			// parameters.
			string MessageElementName = 
				GetReturnValueMessageElementName( operationName,
				element );

			// Get the message element matching the operation name.
			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":message[@name='", MessageElementName, "']/",
				m_WsdlNamespacePrefix, ":part" );

			XmlElement MessagePart = 
				( XmlElement )element.SelectSingleNode( Xpath, m_NamespaceManager );

			string TypeName = GetTypeFromMessagePart( MessagePart );

			// Extract the web service return value info.

			WebServiceReturnValue ReturnValue = 
				GetReturnValueFromSchemaElement( TypeName, element );

			return ReturnValue;
		}
		
		/// <summary>
		/// Gets the message element name holding the return value information
		/// for the specified web service operation.
		/// </summary>
		/// <param name="operationName">The name of the web service operation.</param>
		/// <param name="element">The root element of the loaded wsdl file.</param>
		/// <returns>The name of the message element holding the
		/// return value information for this web service operation.</returns>
		private string GetReturnValueMessageElementName( string operationName,
			XmlElement element )
		{
			string MessageElementName = String.Empty;

			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":portType/",
				m_WsdlNamespacePrefix, 
				":operation[@name='", operationName, "']/",
				m_WsdlNamespacePrefix,
				":output/@message" );

			XmlAttribute MessageAttribute = ( XmlAttribute )element.SelectSingleNode( Xpath, 
				m_NamespaceManager );

			if( MessageAttribute != null )
			{
				// Remove namespace prefix from name.

				MessageElementName = RemoveNamespacePrefix( MessageAttribute.Value );
			}

			return MessageElementName;
		}	
		
		/// <summary>
		/// Gets the web service return value from the schema element.
		/// </summary>
		/// <param name="schemaType">The name of the schema element to get
		/// the parameters from.</param>
		/// <param name="element">The root element of the wsdl document.</param>
		/// <returns>A web service return value.</returns>
		private WebServiceReturnValue GetReturnValueFromSchemaElement( string schemaType, 
			XmlElement element )
		{
			string Xpath = String.Concat( m_WsdlNamespacePrefix,
				":types/",
				m_SchemaNamespacePrefix,
				":schema/",
				m_SchemaNamespacePrefix,
				":element[ @name='", schemaType, "' ]//",
				m_SchemaNamespacePrefix,
				":element" );
			
			// Get return value name and type.


			WebServiceReturnValue ReturnValue = new WebServiceReturnValue( );

			XmlElement ReturnValueElement = ( XmlElement )element.SelectSingleNode( Xpath, m_NamespaceManager );

			if( ReturnValueElement != null )
			{
				XmlAttribute ReturnValueTypeAttribute = ReturnValueElement.Attributes[ "type" ];
				XmlAttribute ReturnValueNameAttribute = ReturnValueElement.Attributes[ "name" ];
	
				ReturnValue.Name = ReturnValueNameAttribute.Value;
				string ParameterType = RemoveNamespacePrefix( ReturnValueTypeAttribute.Value );
	
				if( ParameterType.StartsWith( m_ArrayOfType ) )
				{
					ParameterType = ParameterType.Remove( 0, m_ArrayOfType.Length );
					ReturnValue.ParameterType = String.Concat( ParameterType.ToLower( CultureInfo.InvariantCulture ), ".array" );
				}
				else
				{
					ReturnValue.ParameterType = ParameterType;
				}
			}
			
			return ReturnValue;
		}	
		
		/// <summary>
		/// Gets the RPC web service return value based on the operation name.
		/// </summary>
		/// <param name="operationName">The web service operation name.</param>
		/// <param name="element">The root document element of the loaded 
		/// wsdl xml.</param>
		/// <returns>A web service return value.</returns>
		private WebServiceReturnValue GetRpcWebServiceReturnValue( string operationName, 
			XmlElement element )
		{
			// Get the name of the message element for this operation's
			// parameters.
			string MessageElementName = 
				GetReturnValueMessageElementName( operationName,
				element );

			// Get the message element matching the operation name.
			string Xpath = String.Concat( m_WsdlNamespacePrefix, 
				":message[@name='", MessageElementName, "']/",
				m_WsdlNamespacePrefix, ":part" );

			// Extract the web service parameter info.

			XmlElement MessagePartElement = ( XmlElement )element.SelectSingleNode( Xpath, m_NamespaceManager );

			WebServiceReturnValue ReturnValue = 
				GetReturnValueFromMessagePart( MessagePartElement );

			return ReturnValue;
		}
		
		/// <summary>
		/// Gets the return value information from the message part element.
		/// </summary>
		/// <param name="messagePartElement">A message part element.</param>
		/// <returns>A web service return value.</returns>
		private WebServiceReturnValue GetReturnValueFromMessagePart( XmlElement messagePartElement )
		{
			XmlAttribute ReturnValueTypeAttribute = messagePartElement.Attributes[ "type" ];
			XmlAttribute ReturnValueNameAttribute = messagePartElement.Attributes[ "name" ];

			WebServiceReturnValue ReturnValue = new WebServiceReturnValue( );
			ReturnValue.Name = ReturnValueNameAttribute.Value;

			if( ReturnValueTypeAttribute != null )
			{
				string ParameterType = RemoveNamespacePrefix( ReturnValueTypeAttribute.Value );
				
				if( ParameterType.StartsWith( m_ArrayOfType ) )
				{
					ParameterType = ParameterType.Remove( 0, m_ArrayOfType.Length );
					ReturnValue.ParameterType = 
						String.Concat( ParameterType.ToLower( CultureInfo.InvariantCulture ), ".array" );
				}
				else
				{
					ReturnValue.ParameterType = ParameterType;
				}
			}

			return ReturnValue;
		}		
	}
}
